from datetime import datetime
from typing import Dict
import matplotlib.pyplot as plt
from datetime import datetime
import numpy as np
from io import BytesIO
import base64
import urllib.parse

class ExamRecord():
    '''
    date:时间
    scores:dict(科目：分数)
    exam:模拟 or goal
    '''
    def __init__(self, date, scores, exam):
        self.date = date
        self.scores = scores
        self.exam = exam


def plot_scores_trend(data):
    """
    绘制各科目分数走势图并返回Base64编码图片
    
    参数:
    data (dict): 包含'recent_scores'键的字典
    
    返回:
    str: Base64编码的图片字符串
    """
    # 提取并排序数据（按日期升序）
    scores_data = sorted(data['recent_scores'], 
                         key=lambda x: datetime.strptime(x['date'], '%Y-%m-%d'))
    
    if not scores_data:
        return None
    
    # 获取所有科目
    subjects = set()
    for entry in scores_data:
        subjects.update(entry['scores'].keys())
    subjects = sorted(list(subjects))
    
    # 准备绘图数据
    dates = [entry['date'] for entry in scores_data]
    subject_scores = {subject: [] for subject in subjects}
    
    for entry in scores_data:
        for subject in subjects:
            score = entry['scores'].get(subject, np.nan)
            subject_scores[subject].append(score)
    
    # 创建图表
    plt.figure(figsize=(12, 6))
    ax = plt.gca()
    
    # 使用内置颜色循环
    prop_cycle = plt.rcParams['axes.prop_cycle']
    colors = prop_cycle.by_key()['color']
    
    # 为每个科目绘制折线
    markers = ['o', 's', '^', 'D', 'v', '<', '>', 'p', '*', 'X']
    
    for i, subject in enumerate(subjects):
        color = colors[i % len(colors)]
        marker = markers[i % len(markers)]
        
        plt.plot(dates, 
                 subject_scores[subject], 
                 marker=marker,
                 markersize=8,
                 linewidth=2.5,
                 color=color,
                 label=subject,
                 alpha=0.9)
    
    # 添加图表元素
    plt.title('科目成绩走势分析', fontsize=14, pad=20)
    plt.xlabel('考试日期', fontsize=12)
    plt.ylabel('考试成绩', fontsize=12)
    plt.grid(True, linestyle='--', alpha=0.7)
    plt.xticks(rotation=30, ha='right', fontsize=9)
    
    # 自动调整Y轴范围
    all_scores = [score for sub in subjects for score in subject_scores[sub] if not np.isnan(score)]
    if all_scores:
        min_score = max(0, min(all_scores) - 5)
        max_score = min(100, max(all_scores) + 5)
        plt.ylim(min_score, max_score)
    
    # 添加图例
    plt.legend(loc='upper left', bbox_to_anchor=(1, 1), fontsize=10)
    
    # 添加总分标记
    for i, entry in enumerate(scores_data):
        total = sum([val for key, val in entry['scores'].items() 
                    if isinstance(val, (int, float))])
        plt.annotate(f'总分: {total:.0f}', 
                     (i, plt.ylim()[0]),
                     xytext=(0, -15 if i % 2 == 0 else 15),
                     textcoords='offset points',
                     ha='center',
                     fontsize=9,
                     arrowprops=dict(arrowstyle='-|>', color='gray', alpha=0.5))
    
    # 将图表转换为Base64字符串
    buffer = BytesIO()
    plt.savefig(buffer, format='png', bbox_inches='tight', dpi=100)
    buffer.seek(0)
    image_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
    plt.close()  # 关闭图表释放内存
    
    return f"data:image/png;base64,{image_base64}"



def find_weak_subjects(data):
    """
    找出平均分低于目标分数的科目
    
    参数:
    data (dict): 包含以下键的字典:
        - "recent_scores": 近期考试成绩列表
        - "goal_score": 各科目目标分数字典
    
    返回:
    list: 平均分低于目标分数的科目列表
    """
    # 检查输入数据是否有效
    if not data or "recent_scores" not in data or "goal_score" not in data:
        return []
    
    recent_scores = data["recent_scores"]
    goal_score = data["goal_score"]
    
    if not recent_scores or not goal_score:
        return []
    
    # 创建科目分数收集器
    subject_scores = {}
    
    # 遍历所有考试记录
    for exam in recent_scores:
        for subject, score in exam["scores"].items():
            # 只收集有目标分数的科目
            if subject in goal_score:
                if subject not in subject_scores:
                    subject_scores[subject] = []
                subject_scores[subject].append(score)
    
    # 计算平均分并比较目标
    weak_subjects = []
    
    for subject, scores in subject_scores.items():
        avg_score = sum(scores) / len(scores)
        if avg_score < goal_score[subject]:
            weak_subjects.append({
                "subject": subject,
                "average": round(avg_score, 2),
                "goal": goal_score[subject],
                "gap": round(goal_score[subject] - avg_score, 2)
            })
    
    # 按差距从大到小排序
    weak_subjects.sort(key=lambda x: x["gap"], reverse=True)
    
    return weak_subjects

def generate_bilibili_search_urls(subjects):
    """
    根据输入的科目列表生成B站搜索URL字典
    
    参数:
    subjects: list - 包含科目名称的列表
    
    返回:
    dict - 键为科目名称，值为对应的B站搜索URL
    """
    base_url = "https://search.bilibili.com/video?keyword="
    result = {}
    
    for subject in subjects:
        # 对科目名称进行URL编码
        encoded_subject = urllib.parse.quote(subject)
        # 构建完整的URL
        result[subject] = f"{base_url}{encoded_subject}"
    
    return result
